package com.quectel.cms.controller.scheduling;


import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.quectel.business.common.util.sys.SysCodeUtils;
import com.quectel.cms.controller.BaseController;
import com.quectel.cms.vo.home.CommonCountResp;
import com.quectel.cms.vo.home.CommonPieResp;
import com.quectel.cms.vo.scheduling.ByTimeCountResp;
import com.quectel.cms.vo.scheduling.SchedulingLeaveVo;
import com.quectel.cms.vo.scheduling.SchedulingStatisticsInfoVo;
import com.quectel.cms.vo.scheduling.SchedulingWorkOvertimeVo;
import com.quectel.constant.core.scheduling.SchedulingApplyConstants;
import com.quectel.constant.core.scheduling.SchedulingAttendanceGroupConstants;
import com.quectel.constant.global.SqlConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.constant.sys.SysCodeKeyConstants;
import com.quectel.core.module.dept.dto.DeptDto;
import com.quectel.core.module.dept.dto.DeptPersonDto;
import com.quectel.core.module.dept.service.DeptPersonService;
import com.quectel.core.module.dept.service.DeptService;
import com.quectel.core.module.scheduling.dto.*;
import com.quectel.core.module.scheduling.service.*;
import com.quectel.core.module.schedulingapply.dto.SchedulingApplyLeaveRecordDto;
import com.quectel.core.module.schedulingapply.dto.SchedulingApplyRecordDto;
import com.quectel.core.module.schedulingapply.dto.vo.SchedulingApplyRecordStatisticsVo;
import com.quectel.core.module.schedulingapply.service.SchedulingApplyLeaveRecordService;
import com.quectel.core.module.schedulingapply.service.SchedulingApplyOutRecordService;
import com.quectel.core.module.schedulingapply.service.SchedulingApplyRecordService;
import com.quectel.core.module.schedulingapply.service.SchedulingApplyTripRecordService;
import com.quectel.sys.module.sys.dto.SysCodeDto;
import com.quectel.util.common.DateUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.common.Request;
import com.quectel.util.common.Response;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author bob
 * @email bob.yu@quectel.com
 * @date 2023/07/17 15:42
 */
@RestController
@RequestMapping("home/scheduling")
@Tag(name = "SchedulingStatisticsController", description = "考勤总览相关api")
@Slf4j
public class SchedulingStatisticsController extends BaseController {

    public static final String VILLAGE_ID = "villageId";

    public static final String NOW_DATE = "nowDate";

    public static final String LATE = "late";

    public static final String EARLY = "early";

    public static final String LAKE = "lake";

    private static String JOIN_STRING = "-01-01 00:00:00";

    @DubboReference
    private SchedulingApplyRecordService schedulingApplyRecordService;
    @DubboReference
    private SchedulingApplyLeaveRecordService schedulingApplyLeaveRecordService;
    @DubboReference
    private DeptPersonService deptPersonService;
    @DubboReference
    private DeptService deptService;
    @DubboReference
    private SchedulingAttendanceGroupPersonBindingService groupPersonBindingService;
    @DubboReference
    private SchedulingAttendanceDailyService schedulingAttendanceDailyService;
    @DubboReference
    private SchedulingApplyOutRecordService schedulingApplyOutRecordService;
    @DubboReference
    private SchedulingApplyTripRecordService schedulingApplyTripRecordService;
    @DubboReference
    private SchedulingAttendanceMonthService schedulingAttendanceMonthService;
    @DubboReference
    private SchedulingAttendanceGroupPersonWorkTimeConfigService schedulingAttendanceGroupPersonWorkTimeConfigService;
    @DubboReference
    private SchedulingAttendanceGroupService schedulingAttendanceGroupService;

    @GetMapping("queryDimensionCountByDept")
    @Operation(summary = "考勤总览-迟到/早退/缺勤人数统计")
    public Response<CommonCountResp> queryLateCountByDept(
            @Parameter(description = "类型String (late)迟到 early(早退) lake(缺勤)") @RequestParam String type,
            @Parameter(description = "小区id") @RequestParam Long villageId,
            @Parameter(description = "部门id") @RequestParam(required = false) Long deptId){

        Map<String, Object> params = Request.parseParams(httpServletRequest);
        super.fillAuthorityData(params);

        Date currentDate = new Date();

        params.put(LambdaUtil.getFieldName(SchedulingAttendanceMonthDto::getVillageId), villageId);
        params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), DateUtils.format(DateUtil.beginOfYear(currentDate), DateUtils.FormatType.SIMPLE));
        params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(), DateUtils.format(DateUtil.endOfYear(currentDate), DateUtils.FormatType.SIMPLE));

        List<AttendanceDailyGroupMonthDto> orderCountDtos = schedulingAttendanceMonthService.countAttendanceGroupMonth(params);
        String[] xData = DateUtils.timeSlotOfMonth(DateUtil.beginOfYear(currentDate), DateUtil.endOfYear(currentDate), DateUtils.FormatType.MONTH, 1);

        Map<String, AttendanceDailyGroupMonthDto> carAccessRecordCountDtoMap = new HashMap<>();
        for (AttendanceDailyGroupMonthDto CountCardOrderByTimeDto : orderCountDtos) {
            carAccessRecordCountDtoMap.put(CountCardOrderByTimeDto.getMonth(), CountCardOrderByTimeDto);
        }

        List<Integer> lateTotalList = new ArrayList<>();
        List<Integer> earlyTotalList = new ArrayList<>();
        List<Integer> lakeTotalList = new ArrayList<>();
        for (String xDatum : xData) {
            AttendanceDailyGroupMonthDto countCardOrderByTimeDto = carAccessRecordCountDtoMap.get(xDatum);
            lateTotalList.add(countCardOrderByTimeDto == null || countCardOrderByTimeDto.getTotalLateCount() == null ? SystemConstants.ZERO.intValue() : countCardOrderByTimeDto.getTotalLateCount());
            earlyTotalList.add(countCardOrderByTimeDto == null || countCardOrderByTimeDto.getTotalEarlyCount() == null ? SystemConstants.ZERO.intValue() : countCardOrderByTimeDto.getTotalEarlyCount());
            lakeTotalList.add(countCardOrderByTimeDto == null || countCardOrderByTimeDto.getTotalLakeCount() == null ? SystemConstants.ZERO.intValue() : countCardOrderByTimeDto.getTotalLakeCount());
        }

        CommonCountResp countResp = new CommonCountResp();
        countResp.setXData(xData);
        if (type.equals(LATE)){
            countResp.setYData(new CommonCountResp.DataInfo[]{
                    new CommonCountResp.DataInfo("迟到总数", lateTotalList.toArray(new Integer[0])),
            });
        }
        if (type.equals(EARLY)){
            countResp.setYData(new CommonCountResp.DataInfo[]{
                    new CommonCountResp.DataInfo("早退总数", earlyTotalList.toArray(new Integer[0])),
            });
        }

        if (type.equals(LAKE)){
            countResp.setYData(new CommonCountResp.DataInfo[]{
                    new CommonCountResp.DataInfo("缺勤总数", lakeTotalList.toArray(new Integer[0])),
            });
        }

        return Response.<CommonCountResp>ok().wrap(countResp);
    }


    @GetMapping("leaveTrend")
    @Operation(summary = "考勤总览-请假人数趋势")
    public Response<ByTimeCountResp> leaveTrend(
            @Parameter(description = "小区id") @RequestParam Long villageId,
            @Parameter(description = "部门id") @RequestParam(required = false) Long deptId) {
        Map<String, Object> params = Request.parseParams(httpServletRequest);
        super.fillAuthorityData(params);
        String startTime = null;
        String endTime = null;
        String[] xData = null;
        Date startDate = null, endDate = null;

        DateTime thisYear = DateUtil.parse(LocalDateTime.now().getYear() + JOIN_STRING);

        params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), DateUtils.format(DateUtil.beginOfYear(thisYear), DateUtils.FormatType.COMMON));
        params.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(), DateUtils.format(DateUtil.endOfYear(thisYear), DateUtils.FormatType.COMMON));
        startTime = DateUtil.format(DateUtil.beginOfYear(thisYear), DateUtils.FormatType.MONTH.getType());
        endTime = DateUtil.format(DateUtil.endOfYear(thisYear), DateUtils.FormatType.MONTH.getType());

        // 按月 format dateTime
        params.put(SqlConstants.DATE_FORMAT_TYPE_FILED, SqlConstants.MysqlDateTimeFormatTypeEnum.MONTH.getFormat());

        // x时间轴
        startDate = DateUtils.parse(startTime, DateUtils.FormatType.MONTH);
        endDate = DateUtils.parse(endTime, DateUtils.FormatType.MONTH);
        xData = DateUtils.timeSlotOfMonth(startDate
                , endDate, DateUtils.FormatType.MONTH, 1);

        // 请假
        params.put(LambdaUtil.getFieldName(SchedulingApplyRecordDto::getApplyType), SchedulingApplyConstants.ApplyTypeEnum.APPLY_LEAVE.getType());

        // 审核通过
        params.put(LambdaUtil.getFieldName(SchedulingApplyRecordDto::getStatus), SchedulingApplyConstants.APPROVED_STATUS);

        List<SchedulingApplyRecordStatisticsVo> resultList = schedulingApplyRecordService.countRecordGroupByTime(params);

        Map<String, SchedulingApplyRecordStatisticsVo> workOrderCountDtoMap = new HashMap<>();
        for (SchedulingApplyRecordStatisticsVo workOrderCountDto : resultList) {
            workOrderCountDtoMap.put(workOrderCountDto.getDateTime(), workOrderCountDto);
        }

        List<Integer> leaveNum = new ArrayList<>();
        for (String key : xData) {
            SchedulingApplyRecordStatisticsVo apartmentTrendStatisticsVo = workOrderCountDtoMap.get(key);
            leaveNum.add(apartmentTrendStatisticsVo == null ? 0 : apartmentTrendStatisticsVo.getTotalNum());
        }

        ByTimeCountResp countResp = new ByTimeCountResp();
        countResp.setXData(xData);
        countResp.setYData(new ByTimeCountResp.DataInfo[]{
                new ByTimeCountResp.DataInfo("请假人数", leaveNum.stream().toArray(Integer[]::new))
        });
        return Response.<ByTimeCountResp>ok().wrap(countResp);
    }

    @GetMapping("leaveStatistics")
    @Operation(summary = "考勤总览-请假人数统计")
    public Response<List<SchedulingLeaveVo>> leaveStatistics(
            @Parameter(description = "小区id") @RequestParam Long villageId,
            @Parameter(description = "开始时间") @RequestParam String startTime,
            @Parameter(description = "结束时间") @RequestParam String endTime){

        Request request = Request.configParamsNotPage(Request.parseParams(httpServletRequest));
        super.fillAuthorityData(request.getParams());

        request.put(VILLAGE_ID , villageId);
        request.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), startTime);
        request.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(),endTime);

        List<SchedulingLeaveVo> resultList = new ArrayList<>();

        // 查询小区下参与考勤员工
        List<SchedulingAttendanceGroupPersonBindingDto> schedulingAttendanceGroupPersonBindingDtos =
                groupPersonBindingService.queryList(request.getParams());

        // 查询出小区下的部门
        List<DeptDto> deptDtos = deptService.queryList(request.getParams());

        List<SchedulingApplyLeaveRecordDto> schedulingApplyLeaveRecordDtos =
                schedulingApplyLeaveRecordService.queryListWithoutPadding(request.getParams());


        // 遍历部门
        for (DeptDto deptDto : deptDtos) {
            int count = 0;
            // 根据部门id,找到部门下的人员
            List<DeptPersonDto> deptPersonDtos = deptPersonService.selectByDeptId(deptDto.getId());

            // 遍历部门下的人员，和参与考勤的人员对比，匹配到则 count+1
            for (DeptPersonDto deptPersonDto : deptPersonDtos) {
                for (SchedulingAttendanceGroupPersonBindingDto attendee : schedulingAttendanceGroupPersonBindingDtos) {
                    if (deptPersonDto.getId().equals(attendee.getPersonId())) {
                        count++;
                        break;
                    }
                }
            }

            int workOverNum = 0;
            for (DeptPersonDto deptPersonDto : deptPersonDtos) {
                for (SchedulingApplyLeaveRecordDto attendee : schedulingApplyLeaveRecordDtos) {
                    if (deptPersonDto.getId().equals(attendee.getApplyDeptPersonId())) {
                        workOverNum++;
                        break;
                    }
                }
            }

            // 把部门名字和count放入到 resultList对象中
            SchedulingLeaveVo voOne = new SchedulingLeaveVo();
            voOne.setDeptName(deptDto.getName());
            voOne.setTotalNum(count);
            voOne.setLeaveNum(workOverNum);
            resultList.add(voOne);

        }

        return Response.<List<SchedulingLeaveVo>>ok().wrap(resultList);
    }

    @GetMapping("leaveRateStatistics")
    @Operation(summary = "考勤总览-请假人数占比")
    public Response<CommonPieResp> leaveRateStatistics(
            @Parameter(description = "小区id") @RequestParam Long villageId,
            @Parameter(description = "开始时间") @RequestParam String startTime,
            @Parameter(description = "结束时间") @RequestParam String endTime) {

        Request request = Request.configParams(Request.parseParams(httpServletRequest));
        super.fillAuthorityData(request.getParams());

        request.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), startTime);
        request.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(),endTime);

        // 过滤状态已通过的
        request.put(LambdaUtil.getFieldName(SchedulingApplyLeaveRecordDto::getStatus), SchedulingApplyConstants.APPROVED_STATUS);

        int queryTotal = schedulingApplyLeaveRecordService.queryTotal(request.getParams());
        List<SysCodeDto> categoryCodeList = SysCodeUtils.easyToGetByMark(SysCodeKeyConstants.SCHEDULING_LEAVE_TYPE);

        List<CommonPieResp.PieInfo> eventList = new ArrayList<>();
        for (SysCodeDto sysCodeDto : categoryCodeList) {
            CommonPieResp.PieInfo pieInfo = new CommonPieResp.PieInfo();
            pieInfo.setName(sysCodeDto.getName());
            request.put(LambdaUtil.getFieldName(SchedulingApplyLeaveRecordDto::getLeaveType), sysCodeDto.getValue());
            int count = schedulingApplyLeaveRecordService.queryTotal(request.getParams());
            pieInfo.setValue(Double.valueOf(count));
            eventList.add(pieInfo);
        }

        CommonPieResp commonCountResp = new CommonPieResp();
        commonCountResp.setTotal(Double.valueOf(queryTotal));
        commonCountResp.setPieData(eventList.toArray(new CommonPieResp.PieInfo[0]));
        return Response.<CommonPieResp>ok().wrap(commonCountResp);
    }

    @GetMapping("workOvertimeStatistics")
    @Operation(summary = "考勤总览-加班人数统计")
    public Response<List<SchedulingWorkOvertimeVo>> workOvertimeStatistics(
            @Parameter(description = "小区id") @RequestParam Long villageId,
            @Parameter(description = "开始时间") @RequestParam String startTime,
            @Parameter(description = "结束时间") @RequestParam String endTime){

        Request request = Request.configParamsNotPage(Request.parseParams(httpServletRequest));
        super.fillAuthorityData(request.getParams());

        request.put(VILLAGE_ID , villageId);
        request.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), startTime);
        request.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(),endTime);

        List<SchedulingWorkOvertimeVo> resultList = new ArrayList<>();

        // 查询小区下参与考勤员工
        List<SchedulingAttendanceGroupPersonBindingDto> schedulingAttendanceGroupPersonBindingDtos =
                groupPersonBindingService.queryList(request.getParams());

        // 查询出小区下的部门
        List<DeptDto> deptDtos = deptService.queryList(request.getParams());

        // 查询出今日加班的人员
        List<SchedulingAttendanceDailyDto> schedulingAttendanceDailyDtos =
                schedulingAttendanceDailyService.queryListWithoutPadding(request.getParams());



        // 遍历部门
        for (DeptDto deptDto : deptDtos) {
            int count = 0;
            // 根据部门id,找到部门下的人员
            List<DeptPersonDto> deptPersonDtos = deptPersonService.selectByDeptId(deptDto.getId());

            // 遍历部门下的人员，和参与考勤的人员对比，匹配到则 count+1
            for (DeptPersonDto deptPersonDto : deptPersonDtos) {
                for (SchedulingAttendanceGroupPersonBindingDto attendee : schedulingAttendanceGroupPersonBindingDtos) {
                    if (deptPersonDto.getId().equals(attendee.getPersonId())) {
                        count++;
                        break;
                    }
                }
            }

            int workOverNum = 0;
            for (DeptPersonDto deptPersonDto : deptPersonDtos) {
                for (SchedulingAttendanceDailyDto attendee : schedulingAttendanceDailyDtos) {
                    if (deptPersonDto.getId().equals(attendee.getPersonId())) {
                        workOverNum++;
                        break;
                    }
                }
            }

            // 把部门名字和count放入到 resultList对象中
            SchedulingWorkOvertimeVo voOne = new SchedulingWorkOvertimeVo();
            voOne.setDeptName(deptDto.getName());
            voOne.setTotalNum(count);
            voOne.setWorkOvertimeNum(workOverNum);
            resultList.add(voOne);

        }

        return Response.<List<SchedulingWorkOvertimeVo>>ok().wrap(resultList);
    }

    /**
     * 考勤管理首页-多维度数量统计
     *  今日考勤正常人数
     *  今日请假人数
     *  今日出差人数
     *  今日外出人数
     * @return 计算出百分比
     */
    @GetMapping("statisticsInfo")
    @Operation(summary = "考勤总览-多维度数量统计")
    public Response<SchedulingStatisticsInfoVo> propertyStatisticsInfo(
            @Parameter(description = "小区id") @RequestParam Long villageId) {

        Request request = Request.configParamsNotPage(Request.parseParams(httpServletRequest));
        super.fillAuthorityData(request.getParams());

        Date currentDate = new Date();

        request.put(VILLAGE_ID , villageId);
        request.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), DateUtil.format(DateUtil.beginOfDay(currentDate), DatePattern.NORM_DATETIME_PATTERN));
        request.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(),DateUtil.format(DateUtil.endOfDay(currentDate), DatePattern.NORM_DATETIME_PATTERN));

        SchedulingStatisticsInfoVo vo = new SchedulingStatisticsInfoVo();

        // 参与考勤总人数 手动排班 + 固定
        request.put(LambdaUtil.getFieldName(SchedulingAttendanceGroupPersonWorkTimeConfigDto::getDate), DateUtil.format(DateUtil.beginOfDay(currentDate), DatePattern.NORM_DATE_PATTERN));

        // 手动上班人数
        int schedulingCount = schedulingAttendanceGroupPersonWorkTimeConfigService.queryTotal(request.getParams());

        request.put(LambdaUtil.getFieldName(SchedulingAttendanceGroupDto::getShiftSystemType),
                SchedulingAttendanceGroupConstants.SchedulingTypeEnum.FIX.getType());
        List<SchedulingAttendanceGroupDto> schedulingAttendanceGroupDtos = schedulingAttendanceGroupService.queryListWithoutPadding(request.getParams());

        int fixCount = 0;
        List<Long> todayWorkGroupIds = new ArrayList<>();
        // 遍历固定排班规则，查出固定班今日上班的组
        for (SchedulingAttendanceGroupDto groupDto : schedulingAttendanceGroupDtos) {
            SchedulingAttendanceGroupPersonWorkTimeConfigDto personWorkTimeConfigDto = schedulingAttendanceGroupPersonWorkTimeConfigService.selectOne(groupDto.getId());
            List<FixWorkTimeBindingDto> fixWorkTimeBindingDtos = JacksonUtils.parseArray(personWorkTimeConfigDto.getTimeConfigArr(), FixWorkTimeBindingDto.class);
            List<FixWorkTimeBindingDto> fixWorkTimeBindingConfig = fixWorkTimeBindingDtos.parallelStream()
                    .collect(Collectors.groupingBy(FixWorkTimeBindingDto::getDay))
                    .get(DateUtil.dayOfWeekEnum(DateUtil.parse(DateUtil.format(DateUtil.beginOfDay(currentDate), DatePattern.NORM_DATE_PATTERN), DateUtils.FormatType.SIMPLE.getType())).getValue()
                    );
            if (CollectionUtils.isNotEmpty(fixWorkTimeBindingConfig.get(0).getTimeConfigDtoList())){
                todayWorkGroupIds.add(groupDto.getId());
            }
        }

        // 遍历组人员
        for (Long groupId: todayWorkGroupIds){
            Map<String,Object> queryMap = new HashMap<>();
            queryMap.put(LambdaUtil.getFieldName(SchedulingAttendanceGroupPersonBindingDto::getSchedulingAttendanceGroupId),groupId);
            int queryTotal = groupPersonBindingService.queryTotal(queryMap);
            fixCount += queryTotal;
        }
        vo.setTotalAttendance(fixCount + schedulingCount);

        Map<String,Object> dailyMap = new HashMap<>();
        dailyMap.put(LambdaUtil.getFieldName(SchedulingAttendanceDailyDto::getVillageId),villageId);
        dailyMap.put(LambdaUtil.getFieldName(SchedulingAttendanceDailyDto::getDate), DateUtil.today());

        // 正常考勤
        dailyMap.put(LambdaUtil.getFieldName(SchedulingAttendanceDailyDto::getCheckStatus), SystemConstants.YES);
        int todayNormalCount = schedulingAttendanceDailyService.queryTotal(dailyMap);
        vo.setTodayNormalCount(todayNormalCount);

        request.put(NOW_DATE, NOW_DATE);
        request.put(LambdaUtil.getFieldName(SchedulingApplyLeaveRecordDto::getStatus), SchedulingApplyConstants.APPROVED_STATUS);
        // 外出
        int todayGoOutCount = schedulingApplyOutRecordService.queryTotal(request.getParams());
        vo.setTodayGoOutCount(todayGoOutCount);

        // 请假
        int todayLeaveCount = schedulingApplyLeaveRecordService.queryTotal(request.getParams());
        vo.setTodayLeaveCount(todayLeaveCount);

        // 出差
        int todayBusinessTripsCount = schedulingApplyTripRecordService.queryTotal(request.getParams());
        vo.setTodayBusinessTripsCount(todayBusinessTripsCount);

        return Response.<SchedulingStatisticsInfoVo>ok().wrap(vo);
    }

}
